package com.ailot.cloud.base.security.authentication.filter;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.ailot.cloud.base.common.constants.SecurityConstant;
import com.ailot.cloud.base.common.dto.Result;
import com.ailot.cloud.base.security.authentication.model.JwtAuthenticationToken;
import com.ailot.cloud.base.security.authentication.model.JwtUser;
import com.ailot.cloud.base.security.config.PermitUrlProperties;
import com.ailot.cloud.base.security.utils.JWTUtil;
import com.ailot.cloud.base.security.utils.ResponseUtil;
import com.ailot.cloud.base.security.utils.SecurityUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import io.jsonwebtoken.Claims;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * token拦截验证
 */
@Slf4j
@AllArgsConstructor
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {


    private final PermitUrlProperties permitUrlProperties;

    @Override
    protected void initFilterBean() throws ServletException {
        log.info("JwtAuthenticationTokenFilter初始化...");
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String requestUrl = httpServletRequest.getRequestURI();
        log.info("请求url：{}", requestUrl);
        // 放行的请求
        if (releaseRequest(httpServletRequest)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        String authToken = httpServletRequest.getHeader(SecurityConstant.AUTHORIZATION);
        if (StrUtil.isBlank(authToken)) {
            Result<String> result = Result.fail();
            result.setMsg("未登录");
            ResponseUtil.response(httpServletResponse, result);
            return;
        }
        boolean checkToken = JWTUtil.checkToken(authToken);
        if (checkToken) {
            Result<String> result = Result.fail();
            result.setMsg("会话已失效,请重新登录");
            httpServletResponse.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
            ResponseUtil.response(httpServletResponse, result);
            return;
        }

        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            //Context中的认证为空，进行token验证
            Claims claims = JWTUtil.getClaimsFromToken(authToken);
            //从jwt中恢复用户信息和权限
            String id = claims.get(JWTUtil.ID, String.class);
            String orgId = claims.get(JWTUtil.ORGID, String.class);
            String username = claims.get(JWTUtil.USERNAME, String.class);
            String authorities = claims.get(JWTUtil.AUTHORITIES, String.class);
            List<String> list = JSON.parseObject(authorities, new TypeReference<List<String>>() {
            });
            JwtUser jwtUser = new JwtUser(id, orgId, username, "", true, AuthorityUtils.createAuthorityList(list.toArray(new String[0])));
            //如username不为空，并且能够在数据库中查到
            JwtAuthenticationToken jwtAuthenticationToken =
                    new JwtAuthenticationToken(jwtUser.getAuthorities(), jwtUser, null);
            //将authentication放入SecurityContextHolder中
            SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationToken);
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    /**
     * 过滤白名单的url
     *
     * @return
     */
    private boolean releaseRequest(HttpServletRequest httpServletRequest) {
        // 内部服务调用不拦截
        if (SecurityUtil.isInner(httpServletRequest)) {
            return true;
        }
        // 白名单不拦截
        List<String> releaseUrls = permitUrlProperties.getUrls();
        if (CollectionUtil.isNotEmpty(releaseUrls)) {
            for (String releaseUrl : releaseUrls) {
                AntPathRequestMatcher matcher = new AntPathRequestMatcher(releaseUrl);
                if (matcher.matches(httpServletRequest)) {
                    return true;
                }
            }
        }
        return false;
    }
}